本文介绍了TensorFlow的基本数据类型和使用方法。
本文中tf
都指代的是TensorFlow
Introduction
- TensorFlow中可以认为只有两种基本数据结构:tensor 和 operation。operation代表一个操作,对应计算图中的节点,也可以认为是
函数
;tensor本质是一个句柄,代表的是operation的计算结果,也可认为是函数的返回值
。
tf.Tensor
- tensor的中文意思是“张量”,可将其理解为高维向量,向量是一维tensor,矩阵是二维tensor。
- Tensor底层的值是由
numpy.array
来实现的,因此两者间会有很多共通之处。 - 关于tensor和op的关系:A Tensor is a symbolic handle to one of the outputs of an Operation。即tensor本质是一个句柄,绑定着op的结果,因此一个tensor必然会有一个operation(但一个op可以输出多个tensor),所以tensor中才会有
Tensor.op
属性,表示是什么op产生了这个tensor。 - 也正因为tensor是句柄,并不存储值,只存储计算结果,所以只有经过计算,tensor才会有相应的值,因此只有运行Session后才能得出tensor的值。
tensor命名规则
- tensor的命名格式为
<op_name>:<output_index>
- 对返回tensor的API,会自动创建op,并通过
name
参数指定的操作的名字(即op_name
);如果后续有相同命名的操作,则在op_name
后加_1
;对于其产生的tensor,则按产生顺序自动在后面加:0/:1/:2
等。 - 一般
op_name
命名为大写开头。 - example code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21W1 = tf.Variable([[1,1,1],[2,2,2]],dtype = tf.float32,name='Var')
W2 = tf.Variable([[1,1,1],[2,2,2]],dtype = tf.float32,name='Var')
A1 = tf.add(W1, W2)
B1, B2 = tf.split(W1, 2)
C1 = tf.constant(value=[1,2,3], name="Con1")
P1 = tf.placeholder(tf.int32, shape=(2,3), name="Pla1")
print(W1)
print(W2)
print(A1)
print(B1)
print(B2)
print(C1)
print(P1)
# <tf.Variable 'Var:0' shape=(2, 3) dtype=float32_ref>
# <tf.Variable 'Var_1:0' shape=(2, 3) dtype=float32_ref>
# Tensor("Add:0", shape=(2, 3), dtype=float32)
# Tensor("split:0", shape=(1, 3), dtype=float32)
# Tensor("split:1", shape=(1, 3), dtype=float32)
# Tensor("Con1:0", shape=(3,), dtype=int32)
# Tensor("Pla1:0", shape=(2, 3), dtype=int32
Rank of Tensors
rank指tensor的秩,即维数。不同rank的tensor会有不同的数学意义:
rank | 数学意义 |
---|---|
0 | Scalar(标量) |
1 | Vector(向量) |
2 | Matrix(矩阵) |
3 | Cube(立方体) |
4 | 一行Cube(立方体再向高维抽象) |
- 如何获得tensor的rank:因为其不是Tensor的属性之一,所以无法直接访问对象获得,只能通过
tf.rank(some_Tensor)
方法来获取维数(返回的一个封装了rank值的tensor) - 访问tensor中的某个scalar:每一维指定对应的标号即可。
eg.my_scalar = my_matrix[1, 2]
,my_column_vector = my_matrix[:, 3]
。其中:
表示取这一维度中的所有数。
Shape of Tensors
- shape表示tenor各维度的长度,或者说各维度中元素的个数。
eg.tf.constant([[[1., 2., 3.]], [[7., 8., 9.]]])
:rank为3,shape为[2, 1, 3]。 - tensor的shape可以只指定一部分,如
TensorShape([None, 256])
,只指定了列数为256列,行数会在运行时自动被推断出。 - 如何获得tensor的shape:
- 通过
tf.Tensor.shape
属性(推荐):返回一个TensorShape
对象,通过slice机制来访问封装在TensorShape
中各个维度的长度; - 通过
tf.shape(my_tensor)
方法:返回一个Tensor
对象,表示原tensor的shape。因为是tensor类型,所以必须运行后才能知道其具体值。但好处因为是在运行时计算的,所以此时shape一定是fully-defined的。
- 通过
- 如何改变tensor的shape:通过
tf.reshape()
方法。1
2
3
4
5
6
7
8rank_three_tensor = tf.ones([3, 4, 5])
matrix = tf.reshape(rank_three_tensor, [6, 10])
# Reshape existing content into a 6x10 matrix
matrixB = tf.reshape(matrix, [3, -1])
# Reshape existing content into a 3x20 matrix.
# -1 tells reshape to calculate the size of this dimension.
Data Types of Tensors
- 每个
tf.tensor
都有一个dtype
属性,表示tensor中存储的数据的数据类型,通过tf.tensor.dtype
访问。 - 常用数据类型有:
tf.int32
(对应python中的整型),tf.float32
(对应python中的浮点型)。 - 如何转换tensor类型:
float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)
如何得到tensor的值
- 通过
tf.Tensor.eval()
方法,它其实是tf.get_default_session().run(tf.Tensor)
方法的简写。 - 注1:eval()方法必须在默认的session被激活的情况下才可执行(通过
with tf.Session().as_default():
语句块)。 - 注2:eval方法同样可以传入
feed_dict
,如t.eval(feed_dict={p:2.0})
tf.Variable
tf.Variable
为tensorflow中的变量,值可以通过附加在其上的操作更改。- 声明一个Variable后,系统会生成四个相关操作:
v_name/initial_value
,v_name
,v_name/Assign
,v_name/read
, tf即通过这四个op实现对Variable值的更改。
tensor和variable的关系
- variable对应
tf.Variable
类型,tensor应该对应的是tf.Tensor
。从内部实现上讲,variable本质是对tensor的一种封装。 - TensorFlow官方将
tf.Variable
定义为Tensor-like objects
,即类tensor类型(其他还有numpy.ndarray
,list
以及其他python基本类型:bool, float, int, str
),这些类型在传入函数时会被隐式转换为tensor类型(usingtf.convert_to_tensor()
method),因此也可以当作tensor来使用。 tf.Variable
实现的意义在于提供一种值可以更改的类tensor类型。因为tensor本身实质上属于常量,值一旦初始化是不可更改的(tf.constant()
、tf.placeholder()
都只是产生tensor的方法,返回值是tensor)。- 所以如果要在graph中训练参数,那么参数只能是variable形式(tensor不能改变值),tensor则用来表示输入、输出值,在graph中传递中间的计算值。
定义Variables
- 官方推荐使用
tf.get_variable()
函数来定义、初始化以及调用变量,配合tf.variable_scope
来确保不会不小心重用或声明新的变量。 - 就不要用tf.Variable()。
tf.get_variable()
方法原型:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17tf.get_variable(
name,
shape=None,
dtype=None,
initializer=None,
regularizer=None,
trainable=None,
collections=None,
caching_device=None,
partitioner=None,
validate_shape=True,
use_resource=None,
custom_getter=None,
constraint=None,
synchronization=tf.VariableSynchronization.AUTO,
aggregation=tf.VariableAggregation.NONE
)
其主要有两个作用:
- 计算图中不存在name参数的值时,会声明一个变量,等价于调用variable的构造函数
tf.Variable()
; - 计算图中已存在name参数时,返回对应的变量,因此可通过这个方法来根据名字获取变量;
- 详细参数说明:
name:必填参数,用于指定变量的名字;
shape:指定变量的shape;
initializer:指定变量的初始器,默认为tf.glorot_uniform_initializer
。
collections:指定变量属于的集合,默认为GraphKeys.GLOBAL_VARIABLES
。
初始化variable
variable在使用前必须初始化,初始化变量有两步:
- 在声明变量时指定
initializer
参数,其可以是一个tf.initializer
对象,也可以是一个tensor。若是initializer则会按该initializer的方式来初始化,若是tensor则会用该tensor的值来初始化。默认初始器参数为tf.glorot_uniform_initializer
。 - 调用
tf.global_variables_initializer()
方法,对tf.GraphKeys.GLOBAL_VARIABLES
集合中的变量进行初始化(
按变量指定的initializer
参数进行初始化)。
使用variable
因为variable是tensor-like类型,所以直接像普通tensor一样使用variable即可。
1
2v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
w = v + 1如何为variable重新分配值:使用
tf.Variable.assign()
、tf.Variable.assign_add()
、tf.Variable.assign_sub()
三个方法,可用对Variable重新赋值。1
2
3
4v = tf.get_variable("v", shape=(), initializer=tf.zeros_initializer())
assignment = v.assign_add(1) # assign方法会返回一个新分配值的tensor
tf.global_variables_initializer().run()
sess.run(assignment)
Variable collections
tf提供了collection机制,使得变量的存储和访问变得很方便。系统常用collections:
tf.GraphKeys.GLOBAL_VARIABLES
:全局共享的变量(每个设备都可以访问),变量默认都存储在这个集合中。tf.GraphKeys.TRAINABLE_VARIABLES
:存储会被训练的变量,tf.Optimizer
就是优化此集合下的变量。
注:所有变量默认都是存储在TRAINABLE_VARIABLES
和GLOBAL_VARIABLES
这两个集合中。TRAINABLE_VARIABLES
属于GLOBAL_VARIABLES
的一个子集,两者并不冲突。tf.GraphKeys.LOCAL_VARIABLES
:用于存储临时变量,在这个集合中的变量不会被优化。- 如何将变量添加到指定集合中:
- 在声明变量的时候指定
collections
参数 - 通过调用
tf.add_to_collection(collection_name, variable_name)
函数。
- 在声明变量的时候指定
tf.variable_scope机制
tf.variable_scope
的作用有两点:
- 对变量使用范围加以限制,以方便变量的create和share,在一个scope中构造的变量就只能在这个scope中使用。
- 起到
tf.name_scope
的作用,对变量命名进行整理。
reuse = True
:总是重用变量(变量没有的话会出错),reuse = tf.AUTO_REUSE
:如果变量没有会新建一个变量,有的话则重用。example code:
1
2
3
4
5
6
7def my_image_filter(input_images):
with tf.variable_scope("layer1"):
tf.get_variable(name='Var', shape=[1])
# "layer1/Var"
with tf.variable_scope("layer2"):
tf.get_variable(name='Var', shape=[1])
# 会重新定义一个"layer2/Var"变量,而非重用之前的变量如果确实希望重用相同的内部变量,可通过如下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# example 1
with tf.variable_scope("layer1"):
V1 = tf.get_variable(name='Var', shape=[1])
with tf.variable_scope("layer1"):
V2 = tf.get_variable(name='Var') # 报错,禁止重用变量
# example 2
with tf.variable_scope("layer1"):
V1 = tf.get_variable(name='Var', shape=[1])
V2 = tf.get_variable(name='Var') # 报错,禁止重用变量
# example 3
with tf.variable_scope("layer1"):
V1 = tf.get_variable(name='Var', shape=[1])
# "layer1/Var"
with tf.variable_scope("layer1", reuse=True):
V2 = tf.get_variable(name='Var')
# 正确, V1, V2重用同一个变量
tf.variable_scope和tf.name_scope的区别
- name_scope:仅仅是为了更好地管理变量的命名空间而提出的,对变量的使用没有任何影响。此外tensorboard会将一个name_scope下的变量整理在一个父节点中,因此良好的name scope命名规则会很有利于模型可视化。
- variable_scope:不仅仅是管理命名空间,还是为了限制变量的使用范围,常和 tf.get_variable()配合使用,来实现变量共享的功能。tensorboard也会对同一variable_scope下的变量进行整理。
- 可以认为variable_scope是name_scope针对variable的“加强版”。
tf.constant
tf.constant
是常量tensor,其在声明的时候就定义好值了,且值不可改变。
eg. tf.constant(3.0, dtype=tf.float32)
tf.placeholder
占位符,声明的时候只需要指定类型即可,在运算的时候再指定值。1
2
3
4
5x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x + y
print(sess.run(z, feed_dict={x: 3, y: 4.5}))
print(sess.run(z, feed_dict={x: [1, 3], y: [2, 4]}))
- 注:placeholder不能eval(),其值只能通过feed_dict的形式传入。
- feed_dict:将值以字典的形式传入placeholder,字典的键可以为:
- placeholder的变量名,直接传入变量名;
- placeholder的
name
属性,字符串类型,但必须满足<op_name>:<output_index>
的格式(tf中tensor的命名规范),否则系统无法将其对应到相应的placeholder上。
Post Date: 2019-02-21
版权声明: 本文为原创文章,转载请注明出处